package com.lenovo.srl.dil.tiltbase.lib.fragment;

import java.util.ArrayList;
import java.util.List;
import java.util.Observable;
import java.util.Observer;

import android.app.Activity;
import android.graphics.Point;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.Fragment;
import android.util.DisplayMetrics;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;

import com.google.code.microlog4android.Logger;
import com.google.code.microlog4android.LoggerFactory;
import com.lenovo.srl.dil.tiltbase.lib.R;
import com.lenovo.srl.dil.tiltbase.lib.model.AppInfo;
import com.lenovo.srl.dil.tiltbase.lib.model.ItemInfo;
import com.lenovo.srl.dil.tiltbase.lib.sensor.SensorsProcess;
import com.lenovo.srl.dil.tiltbase.lib.utils.PreferenceUtil;

public abstract class BaseFragment extends Fragment implements Observer,
		OnClickListener, OnTouchListener {

	List<AppInfo> appInfoList = null;

	protected int SCREEN_WIDTH;
	protected int SCREEN_HEIGHT;

	protected RelativeLayout rootView;

	protected Button btnStartEnd;
	protected FrameLayout contentLayout = null;
	protected LinearLayout[] layouts = new LinearLayout[20];
	protected ImageView ball;
	protected boolean isStarted;
	protected int statusBarHeight = 0;

	protected long lastTimeStamp = 0;

	protected Handler mHandler = new Handler();
	protected LinearLayout mFocusLayout = null;

	protected View dividers[] = new View[5];
	protected int[][] dividerLocations = null;

	protected TextView mAngleView;

	public BaseFragment() {
	}

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		statusBarHeight = getStatusBarHeight();
		DisplayMetrics dm = new DisplayMetrics();
		getActivity().getWindowManager().getDefaultDisplay().getMetrics(dm);
		SCREEN_WIDTH = dm.widthPixels;
		SCREEN_HEIGHT = dm.heightPixels;
		appInfoList = AppInfo.queryAppInfo(getActivity());
	}

	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {
		View rootView = inflater.inflate(R.layout.fragment_main, container,
				false);
		rootView.setOnTouchListener(this);
		mAngleView = (TextView) rootView.findViewById(R.id.angle);
		btnStartEnd = (Button) rootView.findViewById(R.id.btn_start_end);
		btnStartEnd.setText(mCtrlMode.toString() + "开始");
		btnStartEnd.setOnClickListener(this);
		contentLayout = (FrameLayout) rootView
				.findViewById(R.id.content_layout);
		ball = (ImageView) rootView.findViewById(R.id.indicator_ball);
		layouts[0] = (LinearLayout) rootView.findViewById(R.id.item1);
		layouts[1] = (LinearLayout) rootView.findViewById(R.id.item2);
		layouts[2] = (LinearLayout) rootView.findViewById(R.id.item3);
		layouts[3] = (LinearLayout) rootView.findViewById(R.id.item4);
		layouts[4] = (LinearLayout) rootView.findViewById(R.id.item5);
		layouts[5] = (LinearLayout) rootView.findViewById(R.id.item6);
		layouts[6] = (LinearLayout) rootView.findViewById(R.id.item7);
		layouts[7] = (LinearLayout) rootView.findViewById(R.id.item8);
		layouts[8] = (LinearLayout) rootView.findViewById(R.id.item9);
		layouts[9] = (LinearLayout) rootView.findViewById(R.id.item10);
		layouts[10] = (LinearLayout) rootView.findViewById(R.id.item11);
		layouts[11] = (LinearLayout) rootView.findViewById(R.id.item12);
		layouts[12] = (LinearLayout) rootView.findViewById(R.id.item13);
		layouts[13] = (LinearLayout) rootView.findViewById(R.id.item14);
		layouts[14] = (LinearLayout) rootView.findViewById(R.id.item15);
		layouts[15] = (LinearLayout) rootView.findViewById(R.id.item16);
		layouts[16] = (LinearLayout) rootView.findViewById(R.id.item17);
		layouts[17] = (LinearLayout) rootView.findViewById(R.id.item18);
		layouts[18] = (LinearLayout) rootView.findViewById(R.id.item19);
		layouts[19] = (LinearLayout) rootView.findViewById(R.id.item20);

		dividers[0] = rootView.findViewById(R.id.divider1);
		dividers[1] = rootView.findViewById(R.id.divider2);
		dividers[2] = rootView.findViewById(R.id.divider3);
		dividers[3] = rootView.findViewById(R.id.divider4);
		dividers[4] = rootView.findViewById(R.id.divider5);

		int i = 0;
		if (appInfoList != null && appInfoList.size() > 0) {
			int j = 0;
			for (; i < 20 && i < appInfoList.size(); i++) {
				AppInfo appInfo = appInfoList.get(i);
				appInfo.setAppLabel(String.valueOf(j + 1));
				ItemInfo item = new ItemInfo(getActivity(), layouts[i], appInfo);
				item.setSelected(false);
				if (ICON_VISIABLITY[i] == 0) {
					layouts[i].setVisibility(View.INVISIBLE);
				} else {
					j += 1;
				}
				layouts[i].setTag(item);
			}
		}
		for (; i < 20; i++) {
			layouts[i].setVisibility(View.INVISIBLE);
		}
		mHandler.postDelayed(new Runnable() {

			@Override
			public void run() {
				initBallPosition();
			}
		}, 300L);
		return rootView;
	}

	public void reset() {
		SensorsProcess.getInstance().stop();
		btnStartEnd.setText(mCtrlMode.toString() + "开始");
		mAngleView.setText(String.format("pitch=%d,roll=%d", 0, 0));
		hideDivider();
		isStarted = false;
		doLog(Status.Reset, getBallPosition(), lastSensorData, null);
		getActivity().runOnUiThread(new Runnable() {

			@Override
			public void run() {
				for (LinearLayout layout : layouts) {
					if (layout.getVisibility() != View.VISIBLE) {
						continue;
					}
					ItemInfo itemInfo = (ItemInfo) layout.getTag();
					if (itemInfo != null) {
						itemInfo.setSelected(false);
					}
				}
				initBallPosition();
			}
		});
	}

	public void begin() {
		doLog(Status.Begin, getBallPosition(), lastSensorData, null);
	}

	public void end() {
		doLog(Status.End, getBallPosition(), lastSensorData, null);
	}

	@Override
	public void onClick(View v) {
		if (v.getId() == R.id.btn_start_end) {
			if (!isStarted) {
				doLog(Status.Start, getBallPosition(), lastSensorData, null);
				SensorsProcess.getInstance().start();
				btnStartEnd.setText(mCtrlMode.toString() + "停止");
				initDivider();
				v.setVisibility(View.GONE);
			} else {
				SensorsProcess.getInstance().stop();
				btnStartEnd.setText(mCtrlMode.toString() + "开始");
				mAngleView.setText(String.format("pitch=%d,roll=%d", 0, 0));
				hideDivider();
				doLog(Status.Stop, getBallPosition(), lastSensorData, null);
			}
			isStarted = !isStarted;
			lastSensorData = null;
			getActivity().runOnUiThread(new Runnable() {

				@Override
				public void run() {
					for (LinearLayout layout : layouts) {
						if (layout.getVisibility() != View.VISIBLE) {
							continue;
						}
						ItemInfo itemInfo = (ItemInfo) layout.getTag();
						if (itemInfo != null) {
							itemInfo.setSelected(false);
						}
					}
					initBallPosition();
				}
			});
		}
	}

	protected void initBallPosition() {
		FrameLayout.LayoutParams param = (FrameLayout.LayoutParams) ball
				.getLayoutParams();
		if (PreferenceUtil.get_ball_default_position() == BallInitPosition.Center
				.ordinal()) {
			param.gravity = Gravity.LEFT | Gravity.TOP;
			param.leftMargin = contentLayout.getWidth() / 2 - ball.getWidth()
					/ 2;
			param.topMargin = contentLayout.getHeight() / 2 - ball.getHeight()
					/ 2;
		} else if (PreferenceUtil.get_ball_default_position() == BallInitPosition.Left
				.ordinal()) {
			param.gravity = Gravity.LEFT | Gravity.TOP;
			param.leftMargin = 0;
			param.topMargin = contentLayout.getHeight() / 2 - ball.getHeight()
					/ 2;
		} else {
			param.gravity = Gravity.LEFT | Gravity.TOP;
			param.leftMargin = SCREEN_WIDTH - ball.getWidth();
			param.topMargin = contentLayout.getHeight() / 2 - ball.getHeight()
					/ 2;
		}
		ball.requestLayout();
	}

	protected float[] lastSensorData = null;

	protected long mLastPitchTime = Long.MAX_VALUE;
	protected boolean mPitchEnabled = false;
	protected Logger logger = LoggerFactory.getLogger(getClass());

	void doLog(Status status, Point curBallPosition, float sensorData[],
			ItemInfo overItem) {
		logger.info(String
				.format("{'mode':'%s','init':'%d','gain':%d,'up_angle':%d,'down_angle':%d,'ratio':%f,'min_ratio':%f,'delay':%d,'status':'%s','x':%d,'y':%d,'pitch':%f,'roll':%f,'over':'%s'}",
						mCtrlMode.toString(), PreferenceUtil
								.get_ball_default_position(), PreferenceUtil
								.get_tilt_gain(), PreferenceUtil
								.get_move_up_init_angle(), PreferenceUtil
								.get_move_down_init_angle(), PreferenceUtil
								.get_over_icon_speed_ratio(), PreferenceUtil
								.get_over_icon_min_speed_ratio(),
						PreferenceUtil.get_move_up_down_time_delay(), status
								.toString(), curBallPosition.x,
						curBallPosition.y, sensorData == null ? 0f
								: sensorData[1], sensorData == null ? 0f
								: sensorData[2], overItem == null ? "-1"
								: overItem.mAppInfo.getAppLabel()));
	}

	void showPitchRollInfo(final float[] sensorData) {
		getActivity().runOnUiThread(new Runnable() {

			@Override
			public void run() {
				mAngleView.setText(String.format("pitch=%f,roll=%f",
						sensorData[1], sensorData[2]));
			}
		});
	}

	List<int[]> posHistoryData = new ArrayList<int[]>(10);
	int index = 0;

	int[] getAverage() {
		int[] sum = new int[2];
		int count = 0;
		for (int i = 0; i < 10 && i < posHistoryData.size(); i++) {
			int[] data = posHistoryData.get(i);
			if (data == null) {
				break;
			}
			count += 1;
			sum[0] += data[0];
			sum[1] += data[1];
		}
		return new int[] { sum[0] / count, sum[1] / count };
	}

	@Override
	public void update(Observable observable, Object data) {
		final float[] tmp = (float[]) data;
//		lastSensorData = tmp;
		// sensorData.add(index, tmp);
		// index++;
		// index = index % 10;
		// lastSensorData=getAverage();
		// if (lastSensorData == null) {
		// lastSensorData = tmp;
		// lastTimeStamp = System.currentTimeMillis();
		// return;
		// }
//		if (Math.abs(lastSensorData[1] - tmp[1]) < getSensorDelta()
//				&& Math.abs(lastSensorData[1] - tmp[1]) < getSensorDelta()) {
//			return;
//		}
		Point curBallPosition = getBallPosition();
		doLog(Status.Running, curBallPosition, lastSensorData, null);
		showPitchRollInfo(tmp);
		moveDivier(tmp);
		moveBall(tmp, curBallPosition);
		lastSensorData = tmp;
		lastTimeStamp = System.currentTimeMillis();

	}

	protected float getSensorDelta() {
		return .0f;
	}

	private void moveBall(float[] sensorData, Point curBallPosition) {
		final LinearLayout layout = getInLayout(curBallPosition);
		FrameLayout.LayoutParams param = (FrameLayout.LayoutParams) ball
				.getLayoutParams();
		if (moveBallX2CenterWhenUpdate()) {
			param.leftMargin = contentLayout.getWidth() / 2 - ball.getWidth()
					/ 2;
		}
		if (moveBallY2CenterWhenUpdate()) {
			param.topMargin = contentLayout.getHeight() / 2 - ball.getHeight()
					/ 2;
		}
		double[] offset = calcXYOffset(sensorData, curBallPosition);
		param.leftMargin += offset[0];
		param.topMargin += offset[1];

//		int[] pos = new int[2];
//		pos[0] = param.leftMargin;
//		pos[1] = param.topMargin;
//		posHistoryData.add(index, pos);
//		index++;
//		index = index % 10;
//		int[] avg = getAverage();
//		param.leftMargin = avg[0];
//		param.leftMargin = avg[1];

		if (mCtrlMode == CtrlMode.PostionVelocity) {
			if (sensorData[1] < 0) {
				if (param.topMargin > dividerLocations[mDividerIndex == -1 ? 2
						: mDividerIndex][1]+ball.getHeight()) {
					param.topMargin = dividerLocations[mDividerIndex == -1 ? 2
							: mDividerIndex][1]+ball.getHeight();
				}
			}
			if (sensorData[1] > 0) {
				if (param.topMargin < dividerLocations[mDividerIndex == -1 ? 2
						: mDividerIndex][1]+ball.getHeight()) {
					param.topMargin = dividerLocations[mDividerIndex == -1 ? 2
							: mDividerIndex][1]+ball.getHeight();
				}
			}
		} else if (mCtrlMode == CtrlMode.Perfect) {
			if (Math.abs(dividerLocations[mDividerIndex == -1 ? 2
					: mDividerIndex][1] - param.topMargin) <= 30) {
				param.topMargin = dividerLocations[mDividerIndex == -1 ? 2
						: mDividerIndex][1];
			}
		}

		if (param.leftMargin <= 0) {
			param.leftMargin = 0;
		}
		if (param.leftMargin >= SCREEN_WIDTH - ball.getWidth()) {
			param.leftMargin = SCREEN_WIDTH - ball.getWidth();
		}
		if (param.topMargin <= 0) {
			param.topMargin = 0;
		}
		if (param.topMargin >= contentLayout.getHeight() - ball.getHeight()) {
			param.topMargin = contentLayout.getHeight() - ball.getHeight();
		}

		if (getActivity() != null) {
			getActivity().runOnUiThread(new Runnable() {

				@Override
				public void run() {
					ball.requestLayout();
					for (LinearLayout layout : layouts) {
						if (layout.getVisibility() != View.VISIBLE) {
							continue;
						}
						if (mFocusLayout != layout) {
							ItemInfo itemInfo = (ItemInfo) layout.getTag();
							if (itemInfo != null) {
								itemInfo.setSelected(false);
							}
						}
					}
					if (layout != null) {
						ItemInfo itemInfo = (ItemInfo) layout.getTag();
						if (itemInfo != null) {
							itemInfo.setSelected(true);
						}
					}
					mFocusLayout = layout;
				}
			});
		}
	}

	protected int getTiltGainByTiltAngle(double tiltAngle) {
		return PreferenceUtil.get_tilt_gain();
	}

	@Override
	public void onAttach(Activity activity) {
		super.onAttach(activity);
		SensorsProcess.getInstance().addObserver(this);
	}

	@Override
	public void onDetach() {
		super.onDetach();
		SensorsProcess.getInstance().stop();
		SensorsProcess.getInstance().deleteObserver(this);
	}

	int getStatusBarHeight() {
		Class<?> c = null;
		Object obj = null;
		java.lang.reflect.Field field = null;
		int x = 0;
		int statusBarHeight = 0;
		try {
			c = Class.forName("com.android.internal.R$dimen");
			obj = c.newInstance();
			field = c.getField("status_bar_height");
			x = Integer.parseInt(field.get(obj).toString());
			statusBarHeight = getResources().getDimensionPixelSize(x);
			return statusBarHeight;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return statusBarHeight;
	}

	protected LinearLayout getInLayout(Point point) {
		for (LinearLayout layout : layouts) {
			if (layout.getVisibility() != View.VISIBLE) {
				continue;
			}
			ItemInfo itemInfo = (ItemInfo) layout.getTag();
			int location[] = itemInfo.getIconLeftTop();
			if (itemInfo != null) {
				if (point.x >= location[0]
						&& point.x <= location[0] + itemInfo.getIconWidth()
						&& point.y >= location[1] - statusBarHeight
						&& point.y <= location[1] - statusBarHeight
								+ itemInfo.getIconHeight()) {
					return layout;
				}
			}
		}
		return null;
	}

	protected int mDividerIndex = -1;

	void initDivider() {
		mDividerIndex = -1;
		int y = ball.getTop();
		if (dividerLocations == null) {
			dividerLocations = new int[5][2];
			for (int i = 0; i < dividers.length; i++) {
				final int[] location = new int[2];
				dividers[i].getLocationOnScreen(location);
				location[1] = location[1] - statusBarHeight;
				dividerLocations[i] = location;
			}
		}
		for (int i = 0; i < dividers.length; i++) {
			dividers[i].setVisibility(View.GONE);
		}
		for (int i = 0; i < dividerLocations.length - 1; i++) {
			if (y > dividerLocations[i][1] && y < dividerLocations[i + 1][1]) {
				mDividerIndex = i;
				break;
			}
		}
		if (mDividerIndex == -1) {
			mDividerIndex = 2;
		}
		if (mCtrlMode == CtrlMode.Perfect
				|| mCtrlMode == CtrlMode.PostionVelocity) {
			dividers[mDividerIndex].setVisibility(View.VISIBLE);
		}
	}

	protected void showDivider() {
		for (int i = 0; i < dividers.length; i++) {
			dividers[i].setVisibility(View.GONE);
		}
		if (mDividerIndex != -1) {
			dividers[mDividerIndex].setVisibility(View.VISIBLE);
		}
	}

	protected void hideDivider() {
		if (mDividerIndex != -1) {
			dividers[mDividerIndex].setVisibility(View.GONE);
			mDividerIndex = -1;
		}
	}

	protected Point getBallPosition() {
		if (ball == null) {
			return new Point(-1, -1);
		}
		int x = ball.getLeft() + ball.getWidth() / 2;
		int y = ball.getTop() + ball.getHeight();
		return new Point(x, y);
	}

	float downX, downY;

	@Override
	public boolean onTouch(View v, MotionEvent event) {

		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			downX = event.getX();
			downY = event.getY();
			break;
		case MotionEvent.ACTION_UP:
			float delta = event.getY() - downY;
			if (Math.abs(delta) > 50) {
				if (mCtrlMode == CtrlMode.Perfect) {
					mDividerIndex = mDividerIndex + (delta < 0 ? -1 : 1);
					if (mDividerIndex > dividers.length - 1) {
						mDividerIndex = dividers.length - 1;
					}
					if (mDividerIndex < 0) {
						mDividerIndex = 0;
					}
					showDivider();
				}
			} else if (Math.abs(delta) < 10)
				if (SensorsProcess.registerState) {
					final LinearLayout layout = getInLayout(getBallPosition());
					if (layout != null) {
						if (SensorsProcess.registerState) {
							final ItemInfo itemInfo = (ItemInfo) layout
									.getTag();
							SensorsProcess.getInstance().stop();
							itemInfo.setClicked();
							isStarted = false;
							btnStartEnd.setVisibility(View.VISIBLE);
							btnStartEnd.setText(mCtrlMode.toString() + "开始");
							// btnAdsorp.setVisibility(View.GONE);
							mAngleView.setText(String.format(
									"pitch=%d,roll=%d", 0, 0));
							doLog(Status.Adsorp, getBallPosition(),
									lastSensorData, itemInfo);
						}
					}
				} else {
					if (dividerLocations != null) {
						if (mFocusLayout != null) {
							ItemInfo itemInfo = (ItemInfo) mFocusLayout
									.getTag();
							if (itemInfo != null) {
								itemInfo.setSelected(false);
							}
						}
						mFocusLayout = null;
						isStarted = true;
						btnStartEnd.setVisibility(View.GONE);
						btnStartEnd.setText(mCtrlMode.toString() + "停止");
						SensorsProcess.getInstance().start();
						doLog(Status.Release, getBallPosition(),
								lastSensorData, null);
					}
				}
			break;
		default:
			break;
		}
		return true;
	}

	protected CtrlMode mCtrlMode = CtrlMode.Perfect;

	public CtrlMode getCtrlMode() {
		return mCtrlMode;
	}

	public enum CtrlMode {
		PostionVelocity, Position, Velocity, Perfect
	};

	public enum Status {
		Begin, End, Start, Stop, Running, Adsorp, Release, Reset
	}

	public enum BallInitPosition {
		Left, Center, Right
	};

	public int[] ICON_VISIABLITY = { 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0,
			0, 1, 0, 0, 0, 0 };

	protected abstract void moveDivier(float[] sensorData);

	protected abstract double[] calcXYOffset(float[] sensorData,
			Point curBallPosition);

	protected boolean moveBallX2CenterWhenUpdate() {
		return false;
	}

	protected boolean moveBallY2CenterWhenUpdate() {
		return false;
	}
}
